use core::ptr::copy_nonoverlapping;


pub trait ProtoEncode {
    fn encode(self, buf: &mut [u8]) -> Option<usize>;
}

impl<T: ProtoEncode + Copy> ProtoEncode for &T {
    fn encode(self, buf: &mut [u8]) -> Option<usize> {
        (*self).encode(buf)
    }
}

impl ProtoEncode for u8 {
    fn encode(self, buf: &mut [u8]) -> Option<usize> {
        if buf.len() < 1 {
            None
        } else {
            buf[0] = self;
            Some(1)
        }
    }
}

impl ProtoEncode for i8 {
    fn encode(self, buf: &mut [u8]) -> Option<usize> {
        if buf.len() < 1 {
            None
        } else {
            buf[0] = self as u8;
            Some(1)
        }
    }
}

impl ProtoEncode for u16 {
    fn encode(self, buf: &mut [u8]) -> Option<usize> {
        if buf.len() < 2 {
            None
        } else {
            buf[0] = (self >> 8) as u8;
            buf[1] = self as u8;
            Some(2)
        }
    }
}

impl ProtoEncode for i16 {
    fn encode(self, buf: &mut [u8]) -> Option<usize> {
        if buf.len() < 2 {
            None
        } else {
            buf[0] = (self >> 8) as u8;
            buf[1] = self as u8;
            Some(2)
        }
    }
}

impl ProtoEncode for u32 {
    fn encode(self, buf: &mut [u8]) -> Option<usize> {
        if buf.len() < 4 {
            None
        } else {
            buf[0] = (self >> 24) as u8;
            buf[1] = (self >> 16) as u8;
            buf[2] = (self >> 8) as u8;
            buf[3] = self as u8;
            Some(4)
        }
    }
}

impl ProtoEncode for i32 {
    fn encode(self, buf: &mut [u8]) -> Option<usize> {
        if buf.len() < 4 {
            None
        } else {
            buf[0] = (self >> 24) as u8;
            buf[1] = (self >> 16) as u8;
            buf[2] = (self >> 8) as u8;
            buf[3] = self as u8;
            Some(4)
        }
    }
}

impl ProtoEncode for &[u8] {
    fn encode(self, buf: &mut [u8]) -> Option<usize> {
        if buf.len() < (self.len() + 1) {
            None
        } else {
            buf[0] = self.len() as u8;
            unsafe {
                copy_nonoverlapping(self.as_ptr(), buf.as_mut_ptr().add(1), self.len());
            }
            Some(buf.len() + 1)
        }
    }
}

impl ProtoEncode for () {
    #[inline(always)]
    fn encode(self, _buf: &mut [u8]) -> Option<usize> {
        Some(0)
    }
}

impl <T1: ProtoEncode, T2: ProtoEncode> ProtoEncode for (T1, T2) {
    fn encode(self, buf: &mut [u8]) -> Option<usize> {
        let n1 = self.0.encode(buf)?;
        let n2 = self.1.encode(&mut buf[n1..])?;
        Some(n1 + n2)
    }
}









